package cn.qianxun.meta.user.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.qianxun.meta.board.mapper.ScreenBoardMapper;
import cn.qianxun.meta.common.core.dto.RowsData;
import cn.qianxun.meta.common.core.enums.UserStatus;
import cn.qianxun.meta.common.core.field.AbstractFieldAssert;
import cn.qianxun.meta.common.core.utils.StreamUtils;
import cn.qianxun.meta.common.core.utils.StringUtils;
import cn.qianxun.meta.common.core.web.dto.SelectVO;
import cn.qianxun.meta.common.core.web.vo.RemoveLongDTO;
import cn.qianxun.meta.user.dto.*;
import cn.qianxun.meta.user.service.ISysUserService;
import cn.qianxun.meta.user.vo.SysUserDetailsVO;
import cn.qianxun.meta.user.vo.SysUserInfoVO;
import cn.qianxun.meta.user.vo.SysUserListVO;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import cn.qianxun.meta.common.core.constant.Constants;
import cn.qianxun.meta.common.core.constant.UserConstants;
import cn.qianxun.meta.common.core.domain.LoginUser;
import cn.qianxun.meta.common.core.domain.RoleDTO;
import cn.qianxun.meta.common.core.exception.ServiceException;
import cn.qianxun.meta.common.mybatis.core.page.PageQuery;
import cn.qianxun.meta.common.mybatis.helper.DataBaseHelper;
import cn.qianxun.meta.common.satoken.utils.LoginHelper;
import cn.qianxun.meta.dept.entity.SysDept;
import cn.qianxun.meta.dept.mapper.SysDeptMapper;
import cn.qianxun.meta.menu.dto.RouterDTO;
import cn.qianxun.meta.menu.service.ISysMenuService;
import cn.qianxun.meta.menu.vo.RouterVO;
import cn.qianxun.meta.menu.vo.SysMenuTreeVO;
import cn.qianxun.meta.role.entity.SysUserRole;
import cn.qianxun.meta.role.mapper.SysRoleMapper;
import cn.qianxun.meta.role.mapper.SysUserRoleMapper;
import cn.qianxun.meta.role.service.ISysPermissionService;
import cn.qianxun.meta.user.dto.*;
import cn.qianxun.meta.user.entity.SysUser;
import cn.qianxun.meta.user.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author fuzhilin
 * @since 2023-08-21 02:16:24
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {


    private final SysDeptMapper deptMapper;
    private final SysRoleMapper roleMapper;
    private final SysUserRoleMapper userRoleMapper;
    private final ISysPermissionService permissionService;
    private final ISysMenuService sysMenuService;
    private final ScreenBoardMapper screenBoardMapper;

    @Override
    public RowsData<SysUserListVO> selectUserList(SysUserQueryDTO dto) {
        PageQuery pageQuery = new PageQuery();
        pageQuery.setPageNum(dto.getPageNum());
        pageQuery.setPageSize(dto.getPageSize());
        Page<SysUser> page = baseMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapper(dto));
        List<SysUser> records = page.getRecords();
        List<SysUserListVO> sysRoleVOList = BeanUtil.copyToList(records, SysUserListVO.class);
        return new RowsData<SysUserListVO>(sysRoleVOList, page.getTotal(), dto.getPageNum());
    }

    private Wrapper<SysUser> buildQueryWrapper(SysUserQueryDTO user) {
        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper.eq("u.del_flag", UserConstants.USER_NORMAL).eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()).like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()).eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()).like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()).ne("u.user_id", -1270012023L).and(ObjectUtil.isNotNull(user.getDeptId()), w -> {
            List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>().select(SysDept::getDeptId).apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors")));
            List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
            ids.add(user.getDeptId());
            w.in("u.dept_id", ids);
        });
        return wrapper;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer addUser(AddSysUserDTO dto) {
        SysUser user = new SysUser();
        BeanUtils.copyProperties(dto, user);
        if (!this.checkUserNameUnique(user)) {
            throw new ServiceException("新增用户'" + user.getUserName() + "'失败，登录账号已存在");
        } else if (StringUtils.isNotEmpty(dto.getPhonenumber())
                && !this.checkPhoneUnique(user)) {
            throw new ServiceException("新增用户'" + user.getPhonenumber() + "'失败，手机号码已存在");
        } else if (StringUtils.isNotEmpty(dto.getEmail())
                && !this.checkEmailUnique(user)) {
            throw new ServiceException("新增用户'" + user.getUserName() + "'失败，邮箱账号已存在");
        }
        user.setCreateBy(LoginHelper.getUserName());
        user.setPassword(BCrypt.hashpw(dto.getPassword()));
        // 新增用户信息
        int rows = baseMapper.insert(user);
        // 新增用户与角色管理
        // 新增用户与角色管理
        if (ObjectUtil.isNotNull(dto.getRoleId())) {
            Long[] roleIds = user.getRoleIds();
            if (ObjectUtil.isNull(roleIds)) {
                Long[] roleIdsTmp = {dto.getRoleId()};
                user.setRoleIds(roleIdsTmp);
            }
        }
        insertUserRole(user);
        return rows;
    }

    /**
     * 校验用户名称是否唯一
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public boolean checkUserNameUnique(SysUser user) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUserName, user.getUserName())
                .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
        return !exist;
    }

    /**
     * 校验手机号码是否唯一
     *
     * @param user 用户信息
     * @return
     */
    @Override
    public boolean checkPhoneUnique(SysUser user) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getPhonenumber, user.getPhonenumber())
                .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
        return !exist;
    }

    /**
     * 校验email是否唯一
     *
     * @param user 用户信息
     * @return
     */
    @Override
    public boolean checkEmailUnique(SysUser user) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getEmail, user.getEmail())
                .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
        return !exist;
    }

    @Override
    public List<SelectVO> getUserEmil() {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUser::getDelFlag, Constants.ZERO);
        wrapper.isNotNull(SysUser::getEmail);
        List<SysUser> sysUsers = baseMapper.selectList(wrapper);
        return sysUsers.stream().map(user -> {
            SelectVO vo = new SelectVO();
            vo.setLabel(String.valueOf(user.getNickName()));
            vo.setValue(user.getEmail());
            return vo;
        }).collect(Collectors.toList());
    }

    /**
     * 新增用户角色信息
     *
     * @param user 用户对象
     */
    public void insertUserRole(SysUser user) {
        this.insertUserRole(user.getUserId(), user.getRoleIds());
    }

    /**
     * 新增用户角色信息
     *
     * @param userId  用户ID
     * @param roleIds 角色组
     */
    public void insertUserRole(Long userId, Long[] roleIds) {
        if (ArrayUtil.isNotEmpty(roleIds)) {
            // 新增用户与角色管理
            List<SysUserRole> list = new ArrayList<>(roleIds.length);
            for (Long roleId : roleIds) {
                SysUserRole ur = new SysUserRole();
                ur.setUserId(userId);
                ur.setRoleId(roleId);
                list.add(ur);
            }
            userRoleMapper.insertBatch(list);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer editUser(AddSysUserDTO dto) {
        SysUser user = new SysUser();
        BeanUtil.copyProperties(dto, user);
        user.setCreateBy(LoginHelper.getUserName());
        if (StringUtils.isNotBlank(dto.getPassword())) {
            user.setPassword(BCrypt.hashpw(dto.getPassword()));
        }
        if (!this.checkUserNameUnique(user)) {
            throw new ServiceException("新增用户'" + user.getUserName() + "'失败，登录账号已存在");
        } else if (StringUtils.isNotEmpty(dto.getPhonenumber())
                && !this.checkPhoneUnique(user)) {
            throw new ServiceException("新增用户'" + user.getPhonenumber() + "'失败，手机号码已存在");
        } else if (StringUtils.isNotEmpty(dto.getEmail())
                && !this.checkEmailUnique(user)) {
            throw new ServiceException("新增用户'" + user.getUserName() + "'失败，邮箱账号已存在");
        }
        Long userId = user.getUserId();
        // 删除用户与角色关联
        userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));
        // 新增用户与角色管理
        if (ObjectUtil.isNotNull(dto.getRoleId())) {
            Long[] roleIds = user.getRoleIds();
            if (ObjectUtil.isNull(roleIds)) {
                Long[] roleIdsTmp = {dto.getRoleId()};
                user.setRoleIds(roleIdsTmp);
            }
        }
        insertUserRole(user);
        LoginHelper.isAdmin(userId);
        return baseMapper.updateById(user);

    }

    @Override
    public Integer deleteUserByIds(RemoveLongDTO dto) {
        return baseMapper.deleteBatchIds(dto.getRemoveIdList());
    }

    @Override
    public SysUserDetailsVO details(Long userId) {
        SysUser sysUser = baseMapper.selectUserById(userId);
        SysUserDetailsVO sysUserDetailsVO = new SysUserDetailsVO();
        BeanUtils.copyProperties(sysUser, sysUserDetailsVO);
        return sysUserDetailsVO;
    }

    @Override
    public LoginUser getUserInfo(String username) {
        LoginUser loginUser = new LoginUser();
        SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username).eq(SysUser::getDelFlag, Constants.ZERO).last("limit 1"));
        if (ObjectUtil.isEmpty(sysUser)) {
            throw new ServiceException("用户名或密码错误。");
        }
        if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) {
            throw new ServiceException("用户账号被停用。");
        }

        // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
        // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
        return buildLoginUser(baseMapper.selectUserByUserName(username));
    }

    /**
     * 构建登录用户
     */
    private LoginUser buildLoginUser(SysUser user) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(user.getUserId());
        loginUser.setDeptId(user.getDeptId());
        loginUser.setUserName(user.getUserName());
        loginUser.setNickName(user.getNickName());
        loginUser.setPassword(user.getPassword());
        loginUser.setUserType(user.getUserType());
        Set<String> perms = new HashSet<>();
        //角色权限
        Set<String> menuPermission = permissionService.getMenuPermission(user);
        //可视化权限
        Set<String> chartBoardPermission = permissionService.getChartBoardPermission(user);
        //大屏去权限
        Set<String> screenBoardPermission = permissionService.getScreenBoardPermission(user);
        perms.addAll(menuPermission);
        perms.addAll(chartBoardPermission);
        perms.addAll(screenBoardPermission);
        loginUser.setMenuPermission(perms);
        loginUser.setRolePermission(permissionService.getRolePermission(user));
        loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName());
        List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
        loginUser.setRoles(roles);
        //此处获取第一个角色进行赋值，后期如果有变动进行处理
        if (ObjectUtil.isNotEmpty(roles)) {
            loginUser.setRoleId(roles.get(0).getRoleId());
        }
        return loginUser;
    }

    @Override
    public void checkUserAllowed(SysUser user) {
        if (ObjectUtil.isNotNull(user.getUserId()) && user.isAdmin()) {
            throw new ServiceException("不允许操作超级管理员用户");
        }
    }

    @Override
    public void checkUserDataScope(Long userId) {
        if (!LoginHelper.isAdmin()) {
            SysUserQueryDTO dto = new SysUserQueryDTO();
            dto.setUserId(userId);
            List<SysUser> users = baseMapper.selectUserList(buildQueryWrapper(dto));
            if (CollUtil.isEmpty(users)) {
                throw new ServiceException("没有权限访问用户数据！");
            }
        }
    }

    @Override
    public Integer resetPwd(SysUserPasswordDTO user) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(user.getUserId());
        sysUser.setPassword(user.getPassword());
        return baseMapper.updateById(sysUser);
    }

    @Override
    public Integer updateUserStatus(SysUser user) {
        return baseMapper.updateById(user);
    }

    @Override
    public void insertUserAuth(Long userId, Long[] roleIds) {
        userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, userId));
        insertUserRole(userId, roleIds);
    }

    @Override
    public SysUser selectUserById(Long userId) {
        return baseMapper.selectUserById(userId);
    }

    @Override
    public Map<String, Object> getInfo() {
        LoginUser loginUser = LoginHelper.getLoginUser();
        SysUser user = baseMapper.selectUserById(loginUser.getUserId());
        boolean isAdmin = StpUtil.hasRole(Constants.SYS_ROLE_PER);
        SysUserInfoVO sysUserInfoVO = new SysUserInfoVO();
        BeanUtil.copyProperties(user, sysUserInfoVO);
        sysUserInfoVO.setAdmin(isAdmin);
        Map<String, Object> ajax = new HashMap<>();
        ajax.put("user", sysUserInfoVO);
        ajax.put("roles", loginUser.getRolePermission());
        ajax.put("permissions", loginUser.getMenuPermission());
        //获取默认大屏地址
        String boardCode = screenBoardMapper.getDefaultLargeScreen();
        ajax.put("defaultBoardCode", boardCode);
        return ajax;
    }

    @Override
    public List<RouterVO> getRouter() {
        RouterDTO dto = new RouterDTO();
        dto.setMenuPlatform(Constants.PC);
        boolean isMain = true;
        List<SysMenuTreeVO> menus = sysMenuService.selectMenuTree(dto);
        List<RouterVO> routerList = sysMenuService.buildMenus(menus, isMain);
        return routerList;
    }

    @Override
    public List<SelectVO> getProcessUserList(UserDeptRoleSelectDTO dto) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotEmpty(dto.getField())) {
            wrapper.like(SysUser::getNickName, dto.getField());
        }
        if (StringUtils.isNotEmpty(dto.getDeptId())) {
            wrapper.eq(SysUser::getDeptId, dto.getDeptId());
        }
        wrapper.ne(SysUser::getUserId, UserConstants.ADMIN_ID);
        wrapper.eq(SysUser::getDelFlag, Constants.ZERO);
        wrapper.orderByDesc(SysUser::getCreateTime);
        List<SysUser> sysUserList = baseMapper.selectList(wrapper);
        return sysUserList.stream().map(user -> {
            SelectVO vo = new SelectVO();
            vo.setValue(String.valueOf(user.getUserId()));
            vo.setLabel(user.getNickName());
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public RowsData<SelectVO> getProcessUserPageList(UserDeptRoleSelectDTO dto) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotEmpty(dto.getField())) {
            wrapper.like(SysUser::getNickName, dto.getField());
        }
        if (StringUtils.isNotEmpty(dto.getDeptId())) {
            wrapper.eq(SysUser::getDeptId, dto.getDeptId());
        }
        wrapper.ne(SysUser::getUserId, UserConstants.ADMIN_ID);
        wrapper.eq(SysUser::getDelFlag, Constants.ZERO);
        wrapper.orderByDesc(SysUser::getCreateTime);
        com.github.pagehelper.Page<Object> page = PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        List<SysUser> sysUserList = baseMapper.selectList(wrapper);
        List<SelectVO> list = sysUserList.stream().map(user -> {
            SelectVO vo = new SelectVO();
            vo.setValue(String.valueOf(user.getUserId()));
            vo.setLabel(user.getNickName());
            return vo;
        }).collect(Collectors.toList());
        return new RowsData<>(list, page.getTotal(), dto.getPageNum());
    }

    @Override
    public LoginUser getUserInfoByUserId(Long userId) {
        LoginUser loginUser = new LoginUser();
        SysUser sysUser = baseMapper.selectById(userId);
        if (StringUtils.isNotNull(sysUser)) {
            SysDept sysDept = deptMapper.selectById(sysUser.getDeptId());
            BeanUtils.copyProperties(sysUser, loginUser);
            loginUser.setDeptName(sysDept.getDeptName());
        }
        return loginUser;
    }

    @Override
    public List<LoginUser> getUserInfoById(List<String> ids) {
        return baseMapper.getUserInfoById(ids);
    }

    @Override
    public Boolean updateUserInfoSelf(SysUserEditAvatarDTO sysUserEditAvatarDTO) {
        AbstractFieldAssert.isNull(sysUserEditAvatarDTO, "用户信息唯一标识不能为空。");
        SysUser sysUser = baseMapper.selectById(LoginHelper.getUserId());
        AbstractFieldAssert.isNull(sysUser, "用户信息错误。");
        sysUser.setAvatar(sysUserEditAvatarDTO.getAvatar());
        sysUser.setNickName(sysUserEditAvatarDTO.getNickName());
        sysUser.setPhonenumber(sysUserEditAvatarDTO.getPhonenumber());
        sysUser.setEmail(sysUserEditAvatarDTO.getEmail());
        sysUser.setSex(sysUserEditAvatarDTO.getSex());
        return baseMapper.updateById(sysUser) > 0;
    }

    @Override
    public Boolean updatePwd(SysUserEditPasswordDTO sysUserEditPasswordDTO) {
        SysUser user = selectUserById(LoginHelper.getUserId());
        String password = user.getPassword();
        if (!BCrypt.checkpw(sysUserEditPasswordDTO.getOldPassword(), password)) {
            throw new ServiceException("修改密码失败，旧密码错误");
        }
        if (BCrypt.checkpw(sysUserEditPasswordDTO.getNewPassword(), password)) {
            throw new ServiceException("新密码不能与旧密码相同");
        }
        SysUserPasswordDTO sysUserPasswordDTO = new SysUserPasswordDTO();
        sysUserPasswordDTO.setPassword(BCrypt.hashpw(sysUserEditPasswordDTO.getNewPassword()));
        sysUserPasswordDTO.setUserId(LoginHelper.getUserId());
        return resetPwd(sysUserPasswordDTO) > 0;
    }
}
